import re
from google.appengine.ext        		  import webapp
from google.appengine.ext.webapp.template import Template
from google.appengine.ext.webapp.template import Context
from ironcap                     		  import cache
from ironcap                              import scripting
from ironcap.cms.models                   import Page

#=========================================================================
# ironcap.cms.frontend.UrlCacheObject
#=========================================================================
class UrlHandlerReference(object):
	""" this class represents an object stored in the memcache. it is hit first """
	def __init__(self, url, key_name):
		self.url 	  		= url
		self.key_name 		= key_name
		#compute the self
		temp 				= url
		if not temp.startswith('^'):
			temp = '^' + temp
		if not temp.endswith('$'):
			temp += '$'
		if temp == '^/form$':
			logging.warning('The URL "/form" is reserved and will not be matched.')			
		self.expression = re.compile(temp)		
	
#=========================================================================
# ironcap.cms.frontend.TemplateProcessor
#=========================================================================

class TemplateProcessor(object):
	""" A class that wraps the Template and Context objects. passed to dynamic handler """
	def __init__(self, text):
		self.text = text
	def render(self, dict = {}):
		""" render the template and return the result """
		t = Template(self.text)
		c = Context (dict)
		return t.render(c)

#=========================================================================
# ironcap.cms.frontend.fetch_handler_references
#=========================================================================
	
def fetch_handler_references():
	""" returns handler references, if not from the memcache, then fall back to datastore. """
	references = cache.shared.get( namespace='ironcap', key='UrlHandlerReferences')
	if not references:
		references = []
		for page in Page.all():
			references.append( UrlHandlerReference( url = page.url, key_name = page.key().name() ))
		cache.shared.set(namespace='ironcap', key='UrlHandlerReferences', value=references, time=86400 )
	return references

#=========================================================================
# ironcap.cms.frontend.fetch_page
#=========================================================================

def fetch_page(url):
	""" fetches pages by checking / matching memcache handler references """
	references = fetch_handler_references()
	for ref in references:
		match = ref.expression.match(url)
		if match:
			return Page.get_by_key_name(ref.key_name)
	return None
	
#=========================================================================
# ironcap.cms.frontend.fetch_module
#=========================================================================

def fetch_compiled_module(page):
	""" fetches this pages module handler """
	compiled_module = cache.local.get('ironcap.pages', page.key().name())
	if not compiled_module:
		compiled_module = scripting.compiler.compile_module(page.code)
		cache.local.set('ironcap.pages', page.key().name(), compiled_module)
	return compiled_module
		
#=========================================================================
# ironcap.cms.frontend.fetch_handler_params
#=========================================================================
def fetch_handler_params(url):
	""" returns the params to be passed to the handler """
	references = fetch_handler_references()
	for ref in references:
		match = ref.expression.match(url)
		if match:
			return match.groups() 
	return None

#=========================================================================
# ironcap.cms.frontend.fetch_handler
#=========================================================================
	
def fetch_handler(source_handler, page, module):
	""" initializes the request handler """
	if module.Handler:
		handler          = module.Handler()
		for k, v in source_handler.__dict__.iteritems():
			setattr(handler, k, v)
		handler.response.headers['Content-Type'] = page.mime
		handler.template = TemplateProcessor(page.template)
		
		# todo : add properties to handler object
		
		return handler
	return None

#=========================================================================
# ironcap.cms.frontend.process
#=========================================================================
	
def process(context, url):
	""" handles http get requests """
	content_type = context.response.headers['Content-Type'] # preserve content type
	page = fetch_page(url)
	if page:
		compiled = fetch_compiled_module(page)
		if compiled:
			if compiled.success:
				# To create the handler, we pass in the current request context, 
				# page object and compiled module. This will be enough to initialize 
				# the handler ready for execution.			
				handler = fetch_handler(context, page, compiled.module)
				if handler:
					if hasattr(handler, 'get'):
						params = fetch_handler_params(url)
						handler.get(*params)
						data = handler.response.out.getvalue()
						handler.response.out.truncate(0)
						handler.response.headers['Content-Type'] = content_type
						return data
	return 'unable to process page'

#=========================================================================
# ironcap.cms.frontend.handler
#=========================================================================

class handler(webapp.RequestHandler):
	def get(self):
		""" handles http get requests """
		page = fetch_page(self.request.path)
		if page:
			compiled = fetch_compiled_module(page)
			if compiled:
				if compiled.success:
					# To create the handler, we pass in the current request context, 
					# page object and compiled module. This will be enough to initialize 
					# the handler ready for execution.
					handler = fetch_handler(self, page, compiled.module) 
					if handler:
						if hasattr(handler, 'get'):
							params = fetch_handler_params(self.request.path)
							handler.get(*params)
							return
				else:
					self.response.out.write('runtime exception : ' + compiled.message)
					return
		self.response.out.write('page not found')
		
	def post(self):
		""" handles http post requests """
		page = fetch_page(self.request.path)
		if page:
			compiled = fetch_compiled_module(page)
			if compiled:
				if compiled.success:
					# To create the handler, we pass in the current request context, 
					# page object and compiled module. This will be enough to initialize 
					# the handler ready for execution.				
					handler = fetch_handler(self, page, compiled.module)
					if handler:
						if hasattr(handler, 'post'):
							params = fetch_handler_params(self.request.path)
							handler.post(*params)
							return
				else:
					self.response.out.write(compiled.message)
					return
		self.response.out.write('page not found')
	